0x1 EGL介绍
我们知道通过OpenGL来绘制的时候需要和EGL配合才能完成渲染,本文主要来介绍一下mesa中的EGL驱动实现。下面先来简单介绍一下EGL。
EGL用于管理绘图表面,其主要提供了下列几种功能,
a 与设备平台的原生窗口系统进行交互。
b 查询可用的绘制类似和相关配置。
c 创建和管理绘制surface。
d 创建和管理绘制context。
e 提供present接口eglSwapBuffers,一般通过交换前后缓存区来实现。
EGL驱动中包括了对下面这些EGL API的封装,应用调用这些API来和EGL驱动交互。
mesa中egl架构如下图所示。
对上图简单说明如下,
从上图左上角可以看到,EGL需要和具体显示平台的窗口NativeWindow交互。这个NativeWindow需要在应用侧创建好,创建的NativeWindow是根据具体的平台(如Android, X11, Wayland)的不同而不同。这个应用创建好的NativeWindow通过调用eglCreateWindowSurface()传入到egl驱动中。
EGL还需要和绘制缓冲区对象(framebuffer)进行交互,这些缓冲区一般由EGL驱动调用外部窗口系统的提供的接口(如android上的surface)来分配和释放。绘制之前需要先得到空闲的buffer,绘制完成以后需要把buffer送给下一级pipeline,交出控制权。这里面一般会创建2~3个buffer,和下一级pipeline一起循环使用。这些buffer在pipeline的不同阶段流动,控制权也在各个阶段中流转,所以需要一种同步机制来保证buffer何时可读,何时可写,在android上是通过fence机制来保证的。
GL Driver调用EGL的内部接口(getBuffers)来得到当前绘制的目标buffer,然后GL Driver就可以发送绘制命令给GPU硬件,GPU硬件把渲染结果绘制到目标buffer中。
笔者的测试平台是Intel i3,GPU是Gen5xx,通过配置mesa的编译参数,可以编译出GPU平台相关的库是iris_dri.so。
这种配置下mesa中代码调用关系如下图所示。
从下图可以看出,mesa把驱动进行了分层,上面是通用的实现,对具体gpu平台相关实现都封装在xxx_dri.so中,这里Gen5xx平台对应的是iris_dri.so,对broadcom vc4 gpu来说,对应的是vc4_dri.so。
0x2 mesa中egl流程介绍
下图说明了一个OpenGL ES应用程序调用EGL接口来绘制的基本流程。
下面对流程中的egl api调用做详细的说明,
0x21 创建X11平台对应的Display和Window
首先在应用程序中通过下面的代码来创建x11平台上的display和window。
0x21 调用eglInitialize
该函数的调用堆栈如下。
eglInitialize的参数是display,这就是前面调用
平台相关接口得到的x_display。
上面调用堆栈最后调用的函数是iris_screen_create,这是mesa的gallium架构下初始化具体gpu型号的硬件驱动的入口函数。
这个在后续的文章中会做详细的介绍,这里我们知道了应用调用eglInitialize()的时候会去调用具体的gpu型号的硬件驱动。
|
|
下图的调用堆栈说明了eglInitialize调用过程中的gem buffer分配操作。这个操作最后会调用到kernel 驱动中完成内存分配动作。
0x22 eglCreateContext的调用流程
eglCreateContext的调用堆栈如下
EGL驱动最后会调用到iris driver中创建context的代码中,其中包括了初始化各种函数指针的代码,包括program, clear, blit等操作。
|
|
下面是iris创建context的时候初始化state相关函数指针的代码。
0x23 和外部NativeWindow的交互
主要是buffer的管理, 通过dequeueBuffer取得空闲buffer供本次绘制使用,在绘制完成了以后,再调用queueBuffer把buffer送去显示。另外外部NativeWindow大小发生变化的时候,也需要调用相应的接口来通知mesa,这个时候一般的流程是先把前面分配的旧的大小的buffer释放掉,然后重新去分配新的大小的buffer,另外还需要调用glViewPort重新设置draw区域的viewport大小。
0x24 gl driver如何取得当前绘制的buffer
GL Driver调用EGL的内部接口(getBuffers)来得到当前绘制的目标buffer。
下图是eglMakeCurrent函数执行的时候分配绘制buffer的堆栈。
具体的调用代码如下所示,外部通过getBuffers来调用具体egl driver的buffer接口。对x11_dr3而言,最后调用的函数是loader_dri3_get_buffers,在这个时候会返回需要的buffer,如果有必要也会重新分配buffer。
|
|
0x25 glClear
glClear的调用堆栈如下,可以看到这个时候也需要分配buffer。
0x26 eglswapbuffer流程
通过调用相关的OpenGL ES API,把需要的绘制资源,如顶点数据(VBO/VAO等),纹理资源(glTexImage2D)等准备好,mesa内部也构造好了对应GPU需要执行的command,这个时候可以启动GPU来绘制了。在函数submit_batch中通过调用DRM_IOCTL_I915_GEM_EXECBUFFER2 ioctl命令来启动kernel的绘制动作。
0x27 egl驱动中实现的其他功能,如chooseConfig
这部分主要是软件逻辑,根据硬件平台的能力,对configure进行管理。